#!/usr/bin/env gst

Object subclass: Celda[
	|posicion estado|
	
	Celda class >> crear: unaPosicion[
		^super new init: unaPosicion
	]
	
	init: pos[
		posicion  := pos.
		estado    := Muerta new.
		^self
	]
	
	posicion[^posicion]
	
	valor[^(estado valor)]
	
	valor: nuevoValor[
		estado := nuevoValor==#muerta ifTrue: [Muerta new] ifFalse: [Viva new].
		^self
	]
	
	actualizarConVecinosVivos: cantVivos[
		estado := estado actualizar: cantVivos.
		^self
	]
]

Object subclass: Estado[

	actualizar: cant[
	]
]


Estado subclass: Muerta[
	valor[^#muerta]
	actualizar: cant[
		^cant==3 ifTrue: [Viva new] ifFalse: [self]
	]
]

Estado subclass: Viva[
	valor[^#viva]
	actualizar: cant[
		^(cant<2)|(cant>3) ifTrue: [Muerta new] ifFalse: [self]
	]
]

Object subclass: Grilla[
	|filas columnas celdas|
	
	Grilla class >> de: cantFilas por: cantColumnas[
		^super new init: cantFilas x: cantColumnas
	]
	
	init: f x: c[
		filas    := f.
		columnas := c.
		celdas := LookupTable new: (filas*columnas).
		1 to: filas do: [:i| 1 to: columnas do: [:j| celdas at: {i. j} put: (Celda crear: i@j)]].
		^self
	]
	
	filas[^filas]
	
	columnas[^columnas]
	
	ponerViva: pos[
		(celdas at: {pos x. pos y}) valor: #viva.
		^self
	]
	
	printString[
		'' displayNl. '╔' display. 1 to: columnas do: [:c| '═' display]. '╗' displayNl.
		1 to: filas do: [:i| '║' display. 1 to: columnas do: [:j| (((celdas at: {i. j}) valor) == #viva) ifTrue: ["▊"'0' display] ifFalse: [' ' display]]. '║' displayNl].
		'╚' display. 1 to: columnas do: [:c| '═' display]. '╝' displayNl.
		^self
	]
	
	celdasVecinas: unaPosicion[
		|x y vecinas|
		x := unaPosicion x.
		y := unaPosicion y.
		vecinas := Bag new.
		x-1 to: x+1
			do: [:i| y-1 to: y+1
				do: [:j| ((i>0) & (j>0) & (i<=filas) & (j<=columnas) & (x~=i | y~=j))
					ifTrue: [vecinas add: (celdas at: {i. j})]
				]
			].
		^vecinas
	]
	
	avanzar[
		|celdasNuevas|
		celdasNuevas := LookupTable new: (filas*columnas).
		celdas do: [:c|celdasNuevas at: {c posicion x. c posicion y} put: ((Celda crear: (c posicion)) valor: c valor)].
		celdasNuevas do: [:c | c actualizarConVecinosVivos: ((self celdasVecinas: (c posicion)) reject: [:v| (v valor)==#muerta]) size].
		celdas := celdasNuevas.
		^self
	]
]


Object subclass: Juego[
	|grilla|
	
	"Juego >> crearDe: cantFilas por: cantColumnas que crea una grilla del tamaño indicado."
	crearDe: cantFilas por: cantColumnas[ 
		grilla := Grilla de: cantFilas por: cantColumnas.
		^self 
	]
	
	"Juego >> grilla que indica la grilla actual."
	grilla[
		^grilla
	]
	
	"Juego >> avanzar que realiza una evolución a la grilla actual e imprime el estado en el Transcript."    
	avanzar[    
		grilla avanzar.
		^self 
	]
	
	"Juego >> ponerViva: posicion que pone viva la celda indicada"
	ponerViva: posicion[
		grilla ponerViva: posicion
	]
	
	"Juego >> ponerBlock: posicion que pone un Block empezando de la posición indicada."
	"Poner un Block en la posición f @c implica poner vivas las celdas f @c, f +1@c, f +1@c+1 y f @c + 1."
	ponerBlock: posicion[
		grilla ponerViva:   posicion.
		grilla ponerViva: ((posicion x) + 1)@ (posicion y).
		grilla ponerViva: ((posicion x) + 1)@((posicion y) + 1).
		grilla ponerViva: ( posicion x     )@((posicion y) + 1).
	]
	
	"Juego >> ponerBlinker: posicion que pone un Blinker empezando de la posición indicada."
	"Poner un Blinker en la posición f@c implica poner vivas las celdas f@c-1, f@c y f@c+1."
	ponerBlinker: posicion[
		grilla ponerViva:   posicion.
		grilla ponerViva: (posicion x)@ (posicion y  - 1).
		grilla ponerViva: (posicion x)@((posicion y) + 1).
	]
	
	ponerGlider: posicion [
		grilla ponerViva:  (posicion x    )@((posicion y) + 2).
		grilla ponerViva: ((posicion x)+ 1)@((posicion y) + 2).
		grilla ponerViva: ((posicion x)+ 2)@((posicion y) + 2).            
		grilla ponerViva: ((posicion x)+ 2)@((posicion y) + 1).           
		grilla ponerViva: ((posicion x)+ 1)@ (posicion y).
	]
	
	ponerGlider2: posicion [
		grilla ponerViva:  (posicion x    )@((posicion y)).
		grilla ponerViva: ((posicion x)+ 1)@((posicion y) + 2).
		grilla ponerViva: ((posicion x)+ 2)@((posicion y)).            
		grilla ponerViva: ((posicion x)+ 2)@((posicion y) + 1).           
		grilla ponerViva: ((posicion x)+ 1)@ (posicion y).
	]
     
	ponerToad: posicion [
		grilla ponerViva:  (posicion x    )@((posicion y) + 2).
		grilla ponerViva: ((posicion x)+ 1)@((posicion y) + 2).
		grilla ponerViva: ((posicion x)+ 2)@((posicion y) + 2).            
		grilla ponerViva: ((posicion x)+ 2)@((posicion y) + 1).           
		grilla ponerViva: ((posicion x)+ 1)@ (posicion y).
	]
	
	printString[
		grilla printString.
	]
]

juego := (((Juego new) crearDe: 15 por: 30) ponerGlider: 1@1) ponerGlider2: 1@28 .
juego printString.
x := 0.
[x<100] whileTrue: [
	juego avanzar printString.
	x := x + 1.
].


"(((((Grilla de: 5 por: 5) ponerViva: 2@3) ponerViva: 2@4) ponerViva: 2@5) celdasVecinas: 1@4) do: [:e|e inspect]"
"((((Grilla de: 5 por: 5) ponerViva: 2@3) ponerViva: 2@4) ponerViva: 2@5) avanzar printString"
"(((Grilla de: 5 por: 5) ponerViva: 2@3) celdasVecinas: 2@1) do: [:e| e inspect]"
"(((Celda crear: 0@0) valor: #muerta) actualizarConVecinosVivos: 2) valor displayNl"
